<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      function Vue(options) {
        //Vue配置项接受的数据 都是放在实例的_data属性上
        this._data = options.data;

        //数据代理:遍历_data的数据在实例上也存放一份,并且使用存取器属性,操作或获取实例上的这个属性时,其实就是操作或获取_data的属性
        for (const key in this._data) {
          Object.defineProperty(this, key, {
            get() {
              return this._data[key];
            },
            set(value) {
              this._data[key] = value;
            },
          });
        }

        //调用observe方法,进行判断并创建对应的发布者
        observe(this._data);
      }

      //封装一个observe函数，判断某个数据是否需要响应式操作,如果不需要则停止,如果需要则创建发布者
      function observe(data) {
        //判断当前的data是否需要继续劫持???????
        const dataType = Object.prototype.toString
          .call(data)
          .slice(8, -1)
          .toLowerCase();

        if (dataType !== "object") return;

        //在observe函数中只要判断成功,则把当前的数据创建一个发布者
        new Observer(data);
      }

      //定义一个发布者的类 Observer类
      //1. 判断数据是否是数组,如果是,则遍历数组,把数组的值交给observe 递归判断和后续操作
      //2. 把数据进行劫持
      class Observer {
        constructor(data) {
          //遍历数据开始劫持
          for (let key in data) {
            //递归当前属性值,去observe进行再次判断,并决定是否深度劫持
            observe(data[key]);

            //判断当前的值是否是数组,如果是数组,则调用observeArray方法对内部的值进行劫持
            if (Array.isArray(data[key])) {
              this.observeArray(data[key]);
            }

            this.defineReactive(data, key, data[key]);
          }
        }
        observeArray(arr) {
          arr.forEach((item) => {
            observe(item);
          });
        }
        defineReactive(data, key, value) {
          //给每一个被劫持的数据都创建一个订阅中心,用来收集当前数据的依赖项 和 通知所有依赖项进行更新
          const dep = new Dep();
          Object.defineProperty(data, key, {
            get() {
              console.log(`${key}属性的值被获取了`);

              //调用订阅中心的 depend方法 收集订阅者
              dep.depend();

              return value;
            },
            set(newVal) {
              console.log(`${key}属性的值被设置了,新的值是${newVal}`);
              value = newVal;

              //调用订阅中心的 notify 方法 通知所有的订阅者更新
              dep.notify();
            },
          });
        }
      }

      //定义一个订阅中心的类 Dep
      class Dep {
        constructor() {
          //定义一个数据,专门用来收集依赖
          this.subs = [];
        }

        //订阅中心任务1:收集依赖(订阅者)
        depend(Watcher) {
          console.log("收集依赖(订阅者)");
          // this.subs.push(Watcher);
        }
        //订阅中心任务2:通知所有的依赖更新
        notify() {
          console.log("通知所有的依赖(订阅者)更新");
        }
      }

      //实例化
      const vm = new Vue({
        data: {
          count: 10,
          name: "xiaowang",
          person: {
            height: 175,
            weight: 180,
          },
          list: [
            { id: 1, name: "aaa" },
            { id: 1, name: "bbb" },
            { id: 1, name: "ccc" },
          ],
        },
      });
    </script>
  </body>
</html>
